{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Object Classification with CNNs\n",
    "\n",
    "Object classification in images with convolutional neural networks (CNNs) is arguably what ignited the field of deep learning (or re-ignited the field of neural networks). The structural prior given by using convolutions is incredibly powerful when processing images, such that features extracted from a randomly initialised CNN can be pretty useful.\n",
    "\n",
    "Deep learning with CNNs shine in computer vision tasks with plenty of labelled data, so we'll look at a fairly standard pipeline based on object classification."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data\n",
    "\n",
    "We'll use the [CIFAR-10](https://www.cs.toronto.edu/~kriz/cifar.html) dataset, which comprises of 60,000 32x32 colour images in 10 classes. The 10 classes contain basic categories of both natural and manmade objects. Here's a selection of some from each class:\n",
    "\n",
    "![CIFAR-10 samples](https://kaggle2.blob.core.windows.net/competitions/kaggle/3649/media/cifar-10.png)\n",
    "\n",
    "The dataset is split into 50,000 training images and 10,000 test images, with a uniform distribution of examples per class. We can use a premade dataset loader from the `torchvision` package. We'll also normalise the data using mean and standard deviation statistics pre-calculated from the entire dataset. When loading training data, we'll also apply some (class-preserving) data augmentations in order to make the model more robust to these sorts of transformations. Finally, we'll make use of asynchronous data loading workers to queue up data while the network is training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from matplotlib import pyplot as plt\n",
    "import torch\n",
    "from torch import nn, optim\n",
    "from torch.nn import functional as F\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision import datasets, transforms\n",
    "from IPython.display import clear_output, display\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Files already downloaded and verified\n"
     ]
    }
   ],
   "source": [
    "batch_size = 128\n",
    "\n",
    "# Set up a convenient default data path\n",
    "data_path = os.path.join(os.path.expanduser('~'), '.torch', 'datasets', 'cifar10')\n",
    "# Set up datasets including normalisation and a data augmentation pipeline for the training data\n",
    "train_data = datasets.CIFAR10(data_path, train=True, download=True, transform=transforms.Compose([\n",
    "    transforms.RandomCrop(32, padding=4),\n",
    "    transforms.RandomHorizontalFlip(),\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))\n",
    "]))\n",
    "test_data = datasets.CIFAR10(data_path, train=False, transform=transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))\n",
    "]))\n",
    "# Use asynchronous data loading (loads images in separate workers)\n",
    "train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=4)\n",
    "test_loader = DataLoader(test_data, batch_size=batch_size, num_workers=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model\n",
    "\n",
    "For the model we'll use a fairly standard set of components - (strided) convolutions, batch normalisation, dropout and ELU nonlinearities. This is on the small side and not well-tuned for CIFAR-10, but is just supposed to be representative of a typical CNN (without skip connections, which are commonplace in deeper architectures)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.conv1 = nn.Conv2d(3, 16, 7, padding=3, bias=False)\n",
    "        self.bn1 = nn.BatchNorm2d(16)\n",
    "        self.conv2 = nn.Conv2d(16, 32, 3, stride=2, padding=1, bias=False)\n",
    "        self.bn2 = nn.BatchNorm2d(32)\n",
    "        self.conv3 = nn.Conv2d(32, 32, 3, padding=1, bias=False)\n",
    "        self.bn3 = nn.BatchNorm2d(32)\n",
    "        self.conv4 = nn.Conv2d(32, 64, 3, stride=2, padding=1, bias=False)\n",
    "        self.bn4 = nn.BatchNorm2d(64)\n",
    "        self.fc1 = nn.Linear(64 * 8 * 8, 512, bias=False)\n",
    "        self.dp1 = nn.Dropout()\n",
    "        self.bn5 = nn.BatchNorm1d(512)\n",
    "        self.fc2 = nn.Linear(512, 10)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = F.elu(self.bn1(self.conv1(x)))\n",
    "        x = F.elu(self.bn2(self.conv2(x)))\n",
    "        x = F.elu(self.bn3(self.conv3(x)))\n",
    "        x = F.elu(self.bn4(self.conv4(x)))\n",
    "        x = x.view(-1, 64 * 8 * 8)  # Reshape from spatial B x C x H x W into 1D features B x CHW\n",
    "        x = F.elu(self.bn5(self.dp1(self.fc1(x))))\n",
    "        return self.fc2(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training and Testing\n",
    "\n",
    "We'll instantiate the model here and train it for a few epochs and plot the training loss for each minibatch. At the end of each epoch we'll also include the test loss, calculated over the entire test set. Finally, we'll print the classification accuracy of the trained model on the test set. It's important to use `.train()` and `.eval()` on the model as appropriate, as batch normalisation and dropout should perform differently depending on whether the model is training or is being used for evaluation. During testing, using the `torch.no_grad()` context manager prevents a computation graph being stored (as otherwise, PyTorch would store a graph because the data interacts with parameters that require gradients by default).\n",
    "\n",
    "Note that to get good performance on this sort of dataset, you would typically want a deeper model running on GPU for many epochs. However, the general components of the architecture, or the training pipeline with data augmentation and normalisation, would remain much the same."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Final test accuracy: 54.94%'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0QAAAHjCAYAAAADn99RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4xLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvDW2N/gAAIABJREFUeJzs3XecXHW9//H3N72TkGyyEBISeidAICAoiHREkSoqUo2oV0Xx6lVU2g8FEURBRIRQVEAxdAQElS4lhBRI6AlJTJYUSAiBkPb9/XF2smdmz5k558xpM+f1fDz2sbMzp3xmd3b3vOfbjLVWAAAAAFBEXbIuAAAAAACyQiACAAAAUFgEIgAAAACFRSACAAAAUFgEIgAAAACFRSACAAAAUFgEIgAAAACFRSACAAAAUFgEIgAAAACF1S3rAsIaMmSIHTVqVNZlAAAAAMip559/frG1tiXItg0XiEaNGqVJkyZlXQYAAACAnDLGvBV0W7rMAQAAACgsAhEAAACAwiIQAQAAACishhtDBAAAAMDb6tWrNW/ePK1cuTLrUlLRq1cvbbLJJurevXvkYxCIAAAAgCYxb9489e/fX6NGjZIxJutyEmWt1ZIlSzRv3jyNHj068nHoMgcAAAA0iZUrV2rw4MFNH4YkyRijwYMH190aRiACAAAAmkgRwlBJHM+VQAQAAACgsAhEAAAAAGKxZMkSjRkzRmPGjFFra6uGDx++/utVq1YFOsYpp5yiV155JeFKOyQ2qYIxZoSkmyS1Slon6Rpr7a99tt1d0tOSjrfW/i2pmgAAAAAkZ/DgwZoyZYok6dxzz1W/fv30ve99r2wba62sterSxbtt5vrrr0+8TrckZ5lbI+ksa+1kY0x/Sc8bYx6y1s5wb2SM6SrpYkkPJlgLAAAAUChnnim1Z5PYjBkjXX55+P1ef/11HXnkkdpnn330zDPP6N5779V5552nyZMn68MPP9Txxx+vn/70p5KkffbZR1deeaV22GEHDRkyRGeccYbuv/9+9enTR3fddZeGDh0a63NKrMuctXaBtXZy++3lkmZKGu6x6TclTZS0MKlaAAAAAGRrxowZOu200/TCCy9o+PDhuuiiizRp0iRNnTpVDz30kGbMmNFpn2XLlmnffffV1KlTtddee2nChAmx15XKOkTGmFGSdpH0TMX9wyV9TtL+knavsv94SeMlaeTIkUmVCQAAADSNKC05Sdp88821++4dl/y33HKLrrvuOq1Zs0bz58/XjBkztN1225Xt07t3bx166KGSpN12202PP/547HUlPqmCMaafnBagM62171U8fLmkH1hr11Y7hrX2GmvtWGvt2JaWlqRKBQAAAJCQvn37rr/92muv6de//rX+9a9/adq0aTrkkEM81xPq0aPH+ttdu3bVmjVrYq8r0UBkjOkuJwz92Vp7u8cmYyXdaoyZLekYSVcZY45MsiYAAAAA2XrvvffUv39/DRgwQAsWLNCDD2Y3nUCSs8wZSddJmmmtvcxrG2vtaNf2N0i611p7Z1I1AQAAAMjerrvuqu2220477LCDNttsM+29996Z1WKstckc2Jh9JD0uabqcabcl6UeSRkqStfbqiu1vkBOIqk67PXbsWDtp0qTY6w3rooukH/7QuZ3QtxAAAAAIZebMmdp2222zLiNVXs/ZGPO8tXZskP0TayGy1j4hyYTY/uSkaklCKQwBAAAAaFyJT6rQrLbeOusKAAAAANSLQBTRyy9nXQEAAACAehGIAAAAABQWgQgAAABAYRGIAAAAABQWgQgAAABALJYsWaIxY8ZozJgxam1t1fDhw9d/vWrVqsDHmTBhgtra2hKstENi024DAAAAKJbBgwdrypQpkqRzzz1X/fr10/e+973Qx5kwYYJ23XVXtba2xl1iJwQiAAAAoBmdeabUHk5iM2aMdPnlkXa98cYb9dvf/larVq3Sxz72MV155ZVat26dTjnlFE2ZMkXWWo0fP17Dhg3TlClTdPzxx6t379569tln1aNHj3ifhwuBCAAAAECiXnzxRd1xxx166qmn1K1bN40fP1633nqrNt98cy1evFjTp0+XJC1dulQDBw7UFVdcoSuvvFJjxoxJvDYCEQAAANCMIrbkJOHhhx/Wc889p7Fjx0qSPvzwQ40YMUIHH3ywXnnlFX3729/WYYcdpoMOOij12ghEMWhrk1Lo3ggAAAA0JGutTj31VF1wwQWdHps2bZruv/9+/eY3v9HEiRN1zTXXpFobs8zFYKONsq4AAAAAyK8DDjhAf/3rX7V48WJJzmx0c+bM0aJFi2St1bHHHqvzzjtPkydPliT1799fy5cvT6U2WogAAAAAJGrHHXfUOeecowMOOEDr1q1T9+7ddfXVV6tr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\n",
      "text/plain": [
       "<Figure size 1008x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "model = Net()\n",
    "optimiser = optim.SGD(model.parameters(), lr=1e-2, momentum=0.9, nesterov=True, weight_decay=1e-4)\n",
    "train_losses, test_losses, test_acc = [], [], 0\n",
    "epochs, iters_per_epoch = 3, len(train_loader)\n",
    "\n",
    "plt.figure(figsize=(14, 8))\n",
    "plt.xlabel('Iterations')\n",
    "plt.ylabel('Loss')\n",
    "plotted_legend = False\n",
    "\n",
    "def plot():\n",
    "    global plotted_legend\n",
    "    plt.plot(range(len(train_losses)), train_losses, 'b-', label='Train')\n",
    "    plt.plot([(i + 1) * iters_per_epoch - 1 for i in range(len(test_losses))], test_losses, 'r-', label='Test')\n",
    "    clear_output(wait=True)\n",
    "    display(plt.gcf())\n",
    "    if not plotted_legend:\n",
    "        plt.legend(loc='upper right')\n",
    "        plotted_legend = True\n",
    "\n",
    "def train():\n",
    "    model.train()\n",
    "    for i, (x, y) in enumerate(train_loader):\n",
    "        optimiser.zero_grad()\n",
    "        y_hat = model(x)\n",
    "        loss = F.cross_entropy(y_hat, y)\n",
    "        loss.backward()\n",
    "        train_losses.append(loss.item())\n",
    "        optimiser.step()\n",
    "        if i % 10 == 0:\n",
    "            plot()\n",
    "\n",
    "def test():\n",
    "    model.eval()\n",
    "    test_loss, correct = 0, 0\n",
    "    with torch.no_grad():\n",
    "        for x, y in test_loader:\n",
    "            y_hat = model(x)\n",
    "            test_loss += F.cross_entropy(y_hat, y, reduction='sum').item()\n",
    "            pred = y_hat.argmax(1, keepdim=True)\n",
    "            correct += pred.eq(y.view_as(pred)).sum().item()\n",
    "\n",
    "    test_losses.append(test_loss / len(test_data))\n",
    "    return correct / len(test_data)\n",
    "\n",
    "for _ in range(epochs):\n",
    "    train()\n",
    "    test_acc = test()\n",
    "plot()\n",
    "clear_output(wait=True)\n",
    "display('Final test accuracy: %.2f%%' % (test_acc * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## GPU Acceleration\n",
    "\n",
    "We can get over 50% accuracy in not too long with a small CNN running on CPU. However, most NNs in use nowadays benefit from the parallelisation afforded by GPUs. We'll make a larger network (with a combination of residual blocks and grouped convolutions that goes by the name of ResNeXt) which would take longer to train on CPU, and run it on GPU. We'll also demonstrate using a learning rate scheduler (with a non-standard schedule), as these are often used when really optimising for performance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ResNeXtBlock(nn.Module):\n",
    "    def __init__(self, hidden_size):\n",
    "        super().__init__()\n",
    "        self.prelu1 = nn.PReLU(2 * hidden_size)\n",
    "        self.conv1 = nn.Conv2d(2 * hidden_size, hidden_size, 1, bias=False)\n",
    "        self.bn1 = nn.BatchNorm2d(hidden_size)\n",
    "        self.prelu2 = nn.PReLU(hidden_size)\n",
    "        self.conv2 = nn.Conv2d(hidden_size, hidden_size, 3, padding=1, groups=hidden_size // 4, bias=False)\n",
    "        self.bn2 = nn.BatchNorm2d(hidden_size)\n",
    "        self.prelu3 = nn.PReLU(hidden_size)\n",
    "        self.conv3 = nn.Conv2d(hidden_size, 2 * hidden_size, 1, bias=False)\n",
    "        self.bn3 = nn.BatchNorm2d(2 * hidden_size)\n",
    "\n",
    "    def forward(self, x):\n",
    "        r = self.bn1(self.conv1(self.prelu1(x)))\n",
    "        r = self.bn2(self.conv2(self.prelu2(r)))\n",
    "        r = self.bn3(self.conv3(self.prelu3(r)))\n",
    "        x = r + x\n",
    "        return x\n",
    "\n",
    "class ResNeXt(nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.conv1 = nn.Conv2d(3, 32, 7, padding=3, bias=False)\n",
    "        self.bn1 = nn.BatchNorm2d(32)\n",
    "        self.prelu1 = nn.PReLU(32)\n",
    "        self.res1 = ResNeXtBlock(16)\n",
    "        self.conv2 = nn.Conv2d(32, 64, 3, stride=2, padding=1, bias=False)\n",
    "        self.bn2 = nn.BatchNorm2d(64)\n",
    "        self.prelu2 = nn.PReLU(64)\n",
    "        self.res2 = ResNeXtBlock(32)\n",
    "        self.conv3 = nn.Conv2d(64, 128, 3, stride=2, padding=1, bias=False)\n",
    "        self.bn3 = nn.BatchNorm2d(128)\n",
    "        self.prelu3 = nn.PReLU(128)\n",
    "        self.res3 = ResNeXtBlock(64)\n",
    "        self.conv4 = nn.Conv2d(128, 256, 3, stride=2, padding=1, bias=False)\n",
    "        self.bn4 = nn.BatchNorm2d(256)\n",
    "        self.prelu4 = nn.PReLU(256)\n",
    "        self.fc1 = nn.Linear(256, 10)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.prelu1(self.bn1(self.conv1(x)))\n",
    "        x = self.prelu2(self.bn2(self.conv2(self.res1(x))))\n",
    "        x = self.prelu3(self.bn3(self.conv3(self.res2(x))))\n",
    "        x = self.prelu4(self.bn4(self.conv4(self.res3(x))))\n",
    "        x = F.adaptive_avg_pool2d(x, (1, 1)).squeeze()\n",
    "        return self.fc1(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Final test accuracy: 63.89%'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1008x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "model = ResNeXt().cuda()\n",
    "optimiser = optim.SGD(model.parameters(), lr=1e-2, momentum=0.9, nesterov=True, weight_decay=1e-4)\n",
    "scheduler = optim.lr_scheduler.LambdaLR(optimiser, lr_lambda=lambda epoch: 5 if epoch == 1 else 1)\n",
    "train_losses, test_losses, test_acc = [], [], 0\n",
    "epochs, iters_per_epoch = 3, len(train_loader)\n",
    "\n",
    "plt.figure(figsize=(14, 8))\n",
    "plt.xlabel('Iterations')\n",
    "plt.ylabel('Loss')\n",
    "plotted_legend = False\n",
    "\n",
    "def plot():\n",
    "    global plotted_legend\n",
    "    plt.plot(range(len(train_losses)), train_losses, 'b-', label='Train')\n",
    "    plt.plot([(i + 1) * iters_per_epoch - 1 for i in range(len(test_losses))], test_losses, 'r-', label='Test')\n",
    "    clear_output(wait=True)\n",
    "    display(plt.gcf())\n",
    "    if not plotted_legend:\n",
    "        plt.legend(loc='upper right')\n",
    "        plotted_legend = True\n",
    "\n",
    "def train():\n",
    "    model.train()\n",
    "    for i, (x, y) in enumerate(train_loader):\n",
    "        optimiser.zero_grad()\n",
    "        y_hat = model(x.cuda())\n",
    "        loss = F.cross_entropy(y_hat, y.cuda())\n",
    "        loss.backward()\n",
    "        train_losses.append(loss.item())\n",
    "        optimiser.step()\n",
    "        if i % 10 == 0:\n",
    "            plot()\n",
    "\n",
    "def test():\n",
    "    model.eval()\n",
    "    test_loss, correct = 0, 0\n",
    "    with torch.no_grad():\n",
    "        for x, y in test_loader:\n",
    "            y_hat = model(x.cuda())\n",
    "            test_loss += F.cross_entropy(y_hat, y.cuda(), reduction='sum').item()\n",
    "            pred = y_hat.argmax(1, keepdim=True).cpu()\n",
    "            correct += pred.eq(y.view_as(pred)).sum().item()\n",
    "\n",
    "    test_losses.append(test_loss / len(test_data))\n",
    "    return correct / len(test_data)\n",
    "\n",
    "for _ in range(epochs):\n",
    "    scheduler.step()\n",
    "    train()\n",
    "    test_acc = test()\n",
    "plot()\n",
    "clear_output(wait=True)\n",
    "display('Final test accuracy: %.2f%%' % (test_acc * 100))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
